#include "userprog/pagedir.h"
#include "vm/frame.h"
#include "threads/palloc.h"
#include "threads/malloc.h"
#include "devices/timer.h"
#include "threads/thread.h"
#include <list.h>
#include <hash.h>


void*
get_frame(enum palloc_flags flags)
{
  struct frame *f = malloc(sizeof(struct frame));
  void *paddr_ = palloc_get_page (flags);
  struct frame * oldest;

  f->paddr = paddr_;
  f->holder = thread_current();
  f->time = timer_ticks();
  f->tid = thread_current()->tid;
  hash_insert (&frame_table, &f->elem);

  return paddr_;
}

struct frame * get_oldest_frame(struct hash * h){
  int i;
  int64_t age = 0,current = timer_ticks();
  struct frame * oldest;
  for (i = 0; i < h->bucket_cnt; i++){
    struct list *bucket = &h->buckets[i];
    struct list_elem *elem, *next;
    for (elem = list_begin (bucket); elem != list_end (bucket); elem = next){
      next = list_next (elem);
      struct hash_elem * h = list_elem_to_hash_elem(elem);
      struct frame * f = hash_entry(h,struct frame,elem);
      if( current - f->time > age){
	age = current - f->time;
	oldest = f;
      }
    }
  }
  return oldest;
}

void
free_frame (const void *paddr)
{
  struct frame * f = frame_lookup(paddr);

  ASSERT( f !=NULL);
  
  hash_delete (&frame_table, &f->elem);
  
  free(f); //free memory allocated

  return;
}

/* returns corresponding struct frame of paddr */
struct frame * frame_lookup(void * paddr){
  struct frame f;
  struct hash_elem *e;
  f.paddr = paddr;
  e = hash_find(&frame_table,&f.elem);
  return e != NULL ? hash_entry(e,struct frame,elem) : NULL;
}

unsigned frame_hash_func (struct hash_elem *e, void *aux)
{
  const struct frame *f = hash_entry (e, struct frame, elem);
  return hash_bytes (&f->paddr, sizeof f->paddr);
//  return 0;
}

bool frame_less_func (struct hash_elem *a_, struct hash_elem *b_, void *aux)
{
  const struct frame *a = hash_entry (a_, struct frame, elem);
  const struct frame *b = hash_entry (b_, struct frame, elem);
  
  return a->paddr < b->paddr;
//  return 0;
}
